#!/bin/bash

CLANG_FORMAT=$(which clang-format)
if [ -z "$CLANG_FORMAT" ]; then
    echo "clang-format is not installed, please install clang-format"
    exit 1
fi

DOS2UNIX=$(which dos2unix)
if [ -z "$DOS2UNIX" ]; then
    echo "dos2unix is not installed, please install dos2unix"
    exit 1
fi

IGNORE=("build" "out" "build_*" "out_*" "build-*" ".git")

CLANG_FORMAT_EXTENSIONS=(".cpp" ".h" ".hpp" ".c" ".cc")
DOS2UNIX_EXTENSIONS=(".cpp" ".h" ".hpp" ".c" ".cc" ".sh" ".log" ".md" ".json" ".py" ".cmake" ".makefile" ".mk" ".txt")

BASE_PATH=$(cd $(dirname $0); pwd)
ROOT_PATH=$(cd ${BASE_PATH}/..; pwd)
echo "ROOT_PATH: $ROOT_PATH"

function usage() {
    echo "Usage: $0 [-a] [-c] [-l] [-h]"
    echo "  -a: check all files (default)"
    echo "  -c: check changed files"
    echo "  -l: check last commit"
    echo "  -h: show this help message"
}

function checkopts() {
    mode="all"

    while getopts "aclh" opt; do
        case $opt in
            a)  mode="all"
                ;;
            c)  mode="changed"
                ;;
            l)  mode="last_commit"
                ;;
            h)  usage
                ;;
            ?)  echo "Unknown option: ${opt}!"
                usage
                exit 1
                ;;
        esac
    done
    echo "mode: $mode"
}

# $1: SOURCE_FILE_LIST
# $2: SOURCE_FILE_EXTENSIONS
function gen_source_file_list() {
    regex1=$([[ ${IGNORE} ]] && __=${IGNORE//./\\.}; echo "^${__// /|^}" || echo "^/")
    regex2=$([[ $2 ]] && __=${2//./\\.}; echo "${__// /$|}$" || echo "^/")
    if [[ ${mode} == "all" ]]; then
        find . -type f | cut -c 3- | grep -Ev ${regex1} | grep -E ${regex2} > $1 || true
    elif [[ ${mode} == "changed" ]]; then
        git diff --diff-filter=ACMRTUXB --name-only HEAD | grep -Ev ${regex1} | grep -E ${regex2} > $1 || true
    elif [[ ${mode} == "last_commit" ]]; then
        git diff --diff-filter=ACMRTUXB --name-only HEAD~ HEAD | grep -Ev ${regex1} | grep -E ${regex2} > $1 || true
    fi
}

function check_clang_format() {
    echo "Checking clang-format..."
    cd ${ROOT_PATH}

    local SOURCE_FILE_LIST="__source_file_list__"
    gen_source_file_list ${SOURCE_FILE_LIST} "$CLANG_FORMAT_EXTENSIONS"

    while read line; do
        if [[ -f $line ]]; then
            local TEMP="__temp__$(basename ${line})__"
            cp ${line} ${TEMP}

            $CLANG_FORMAT -i ${TEMP}
            diff ${TEMP} ${line}
            local ret=$?

            rm ${TEMP}

            if [[ $ret -ne 0 ]]; then
                break
            fi
        fi
    done < ${SOURCE_FILE_LIST}

    rm ${SOURCE_FILE_LIST}
    return ${ret}
}

function check_dos2unix() {
    echo "Checking dos2unix..."
    cd ${ROOT_PATH}

    local SOURCE_FILE_LIST="__source_file_list__"
    gen_source_file_list ${SOURCE_FILE_LIST} "$DOS2UNIX_EXTENSIONS"

    while read line; do
        if [[ -f $line ]]; then
            local TEMP="__temp__$(basename ${line})__"
            cp ${line} ${TEMP}
            $DOS2UNIX -q ${TEMP}
            diff ${TEMP} ${line} > /dev/null 2>&1
            local ret=$?

            rm ${TEMP}

            if [[ $ret -ne 0 ]]; then
                break
            fi
        fi
    done < ${SOURCE_FILE_LIST}

    rm ${SOURCE_FILE_LIST}
    return ${ret}
}

IGNORE=${IGNORE[*]}
CLANG_FORMAT_EXTENSIONS=${CLANG_FORMAT_EXTENSIONS[*]}
DOS2UNIX_EXTENSIONS=${DOS2UNIX_EXTENSIONS[*]}

checkopts "$@"

check_clang_format
ret=$?
if [[ $ret -ne 0 ]]; then
    echo "FAILED: File ${line} is not formatted, please format it"
    exit ${ret}
fi

check_dos2unix
ret=$?
if [[ $ret -ne 0 ]]; then
    echo "FAILED: File ${line} contains dos line endings, please convert it to unix line endings"
    exit ${ret}
fi

echo "PASSED: Specified files are formatted correctly"
exit 0